package com.iplay.service.review;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.iplay.component.date.FeastBookingDateFormatter;
import com.iplay.dao.hotel.HotelDAO;
import com.iplay.dao.hotel.rating.HotelRatingDAO;
import com.iplay.dao.order.OrderDAO;
import com.iplay.dao.review.ReviewDAO;
import com.iplay.dto.review.ReviewDTO;
import com.iplay.entity.hotel.rating.HotelRatingDO;
import com.iplay.entity.order.OrderBHReviewedCustomerIdDO;
import com.iplay.entity.order.OrderStatus;
import com.iplay.entity.review.ReviewDO;
import com.iplay.service.exception.InvalidRequestParametersException;
import com.iplay.service.exception.ResourceForbiddenException;
import com.iplay.service.exception.ResourceNotFoundException;
import com.iplay.service.notification.core.NotificationService;
import com.iplay.service.notification.type.order.type.OrderReviewedMessage;
import com.iplay.service.notification.type.order.OrderMessageEntity;
import com.iplay.service.user.SimplifiedUser;
import com.iplay.vo.common.EntityDeletionVO;
import com.iplay.vo.review.PostReviewVO;

@Service
public class ReviewServiceImpl implements ReviewService {

	private static final Logger LOGGER = LoggerFactory.getLogger(ReviewServiceImpl.class);

	@Autowired
	private OrderDAO orderDAO;

	@Autowired
	private HotelRatingDAO hotelRatingDAO;

	@Autowired
	private HotelDAO hotelDAO;

	@Autowired
	private ReviewDAO reviewDAO;

	@Autowired
	private FeastBookingDateFormatter dateFormatter;

	@Autowired
	private NotificationService notificationService;

	@Override
	@Transactional
	public int addReviews(int orderId, SimplifiedUser author, PostReviewVO vo) {
		OrderBHReviewedCustomerIdDO order = orderDAO.findBHReviewedCustomerIdById(orderId);
		if (order == null)
			throw new ResourceNotFoundException("Order with id:" + orderId + " doesn't exist!");
		if (order.getOrderStatus() != OrderStatus.TO_BE_REVIEWD || order.isReviewed()
				|| order.getCustomerId() != author.getUserId())
			throw new ResourceForbiddenException("Order with id:" + orderId + " must be in " + OrderStatus.TO_BE_REVIEWD
					+ " status or this order already has been reviewed or you don't have authority!");
		String banquetHall = order.getBanquetHall();
		int hotelId = order.getHotelId();
		double score = vo.getRating();
		ReviewDO savedReview = reviewDAO.save(
				new ReviewDO(hotelId, banquetHall, author.getUserId(), author.getUsername(), score, vo.getReview()));
		// update hotel rating
		HotelRatingDO rating = hotelRatingDAO.findOne(hotelId);
		if (rating == null) {
			try {
				rating = hotelRatingDAO.save(new HotelRatingDO(hotelId, 0, 0, 0.0));
			} catch (DataIntegrityViolationException e) {
				LOGGER.error(e.getMessage());
			}
		}
		rating.setRating((rating.getTotalScore() + score) / (rating.getTimes() + 1));
		rating.setTimes(rating.getTimes() + 1);
		rating.setTotalScore(rating.getTotalScore() + score);
		hotelRatingDAO.save(rating);
		hotelDAO.updateRatingAndIncreaseNumOfReviewsAndNumOfOrders(hotelId, rating.getRating());
		orderDAO.updateReviewedAndOrderStatus(orderId, true, OrderStatus.DONE, System.currentTimeMillis());

		notificationService.sendMessages(new OrderReviewedMessage(
				new OrderMessageEntity(order.getId(), order.getHotel(), order.getCustomer(), null, null, order.getOrderStatus().getCategory()))
						.getOutgoingMessage());

		return savedReview.getId();
	}

	@Override
	public boolean deleteReview(int reviewId) {
		return false;
	}

	@Override
	public boolean deleteReviews(EntityDeletionVO vo) {
		return false;
	}

	@Override
	public Page<ReviewDTO> listReviews(int hotelId, Pageable pageable) {
		try {
			return reviewDAO.findByHotelId(hotelId, pageable)
					.map(reviewDO -> new ReviewDTO(reviewDO.getId(), reviewDO.getHotelId(), reviewDO.getBanquetHall(),
							reviewDO.getAuthorId(), reviewDO.getAuthor(), reviewDO.getRating(), reviewDO.getReview(),
							dateFormatter.toDefaultFormattedDateTime(reviewDO.getReviewTime())));
		} catch (PropertyReferenceException e) {
			throw new InvalidRequestParametersException(e.getMessage());
		}
	}

}
